package weka;

import weka.clusterers.ClusterEvaluation;
import weka.clusterers.Clusterer;
import weka.core.*;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.Remove;
import weka.gui.explorer.Messages;
import weka.gui.visualize.Plot2D;
import weka.gui.visualize.PlotData2D;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by taohj on 2014/12/29.
 */
public class ClustererHelpper extends  AbstractHelpper {


    protected Instances m_TestInstances;

    public ClustererHelpper(String location, String classifierClass,String option) throws Exception {
        super(location, classifierClass,option);
    }


    public static void main(String[] args) throws Exception {

        System.out.println(new ClustererHelpper("C:\\Users\\taohj\\Desktop\\data\\Drug.csv", "weka.clusterers.SimpleKMeans","").start());
    }

    @Override
    public  StringBuffer start() throws Exception {
        long trainTimeStart = 0, trainTimeElapsed = 0;
        Instances inst = new Instances(this.getM_Instances());
        inst.setClassIndex(-1);
        Instances userTest = null;
        PlotData2D predData = null;
        if (m_TestInstances != null) {
            userTest = new Instances(m_TestInstances);
        }

        boolean saveVis =true;
        String grph = null;
        int[] ignoredAtts = null;

        int testMode = 0;
        int percent = 66;
        Clusterer clusterer = (Clusterer)Class.forName(this.getClassName()).newInstance();
        Clusterer fullClusterer = null;
        StringBuffer outBuff = new StringBuffer();
        String name = (new SimpleDateFormat("HH:mm:ss - "))
                .format(new Date());
        String cname = clusterer.getClass().getName();
        if (cname.startsWith("weka.clusterers.")) {
            name += cname.substring("weka.clusterers.".length());
        } else {
            name += cname;
        }
        String cmd = clusterer.getClass().getName();

        if (clusterer instanceof OptionHandler) {
            ((OptionHandler) clusterer).setOptions(Utils.splitOptions(this.getOption()));
            cmd += " "
                    + Utils.joinOptions(((OptionHandler) clusterer).getOptions());
        }

        testMode = 3;

        Instances trainInst = new Instances(inst);

        // Output some header information
        outBuff.append(Messages.getInstance().getString(
                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_First"));
        outBuff.append(Messages.getInstance().getString(
                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Second")
                + cname);
        if (clusterer instanceof OptionHandler) {
            String[] o = ((OptionHandler) clusterer).getOptions();
            outBuff.append(" " + Utils.joinOptions(o));
        }
        outBuff.append(Messages.getInstance().getString(
                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Third"));
        outBuff.append(Messages.getInstance().getString(
                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Fourth")
                + inst.relationName() + '\n');
        outBuff.append(Messages.getInstance().getString(
                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Sixth")
                + inst.numInstances() + '\n');
        outBuff.append(Messages.getInstance().getString(
                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Eighth")
                + inst.numAttributes() + '\n');


        if (inst.numAttributes() < 100) {
            boolean[] selected = new boolean[inst.numAttributes()];
            for (int i = 0; i < inst.numAttributes(); i++) {
                selected[i] = true;
            }

            for (int i = 0; i < inst.numAttributes(); i++) {
                if (selected[i]) {
                    outBuff.append("              " + inst.attribute(i).name()
                            + '\n');
                }
            }

        } else {
            outBuff
                    .append(Messages
                            .getInstance()
                            .getString(
                                    "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Thirteenth"));
        }

        outBuff.append(Messages.getInstance().getString(
                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Fourteenth"));

        switch (testMode) {
            case 3: // Test on training
                outBuff
                        .append(Messages
                                .getInstance()
                                .getString(
                                        "ClustererPanel_StartClusterer_Run_OutBuffer_Text_Fifteenth"));
                break;
        }

        outBuff.append("\n");
        trainTimeStart = System.currentTimeMillis();
        // remove the class attribute (if set) and build the clusterer
        clusterer.buildClusterer(removeClass(trainInst));
        trainTimeElapsed = System.currentTimeMillis() - trainTimeStart;

        // if (testMode == 2) {
        outBuff
                .append(Messages
                        .getInstance()
                        .getString(
                                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_TwentySecond"));

        outBuff.append(clusterer.toString() + '\n');

        outBuff
                .append(Messages
                        .getInstance()
                        .getString(
                                "ClustererPanel_StartClusterer_Run_OutBuffer_Text_TimeTakenFull")
                        + Utils.doubleToString(trainTimeElapsed / 1000.0, 2)
                        + " "
                        + Messages
                        .getInstance()
                        .getString(
                                "ClassifierPanel_StartClassifier_OutBuffer_Text_TwentyNineth"));

        SerializedObject so = new SerializedObject(clusterer);
        fullClusterer = (Clusterer) so.getObject();

        ClusterEvaluation eval = new ClusterEvaluation();
        eval.setClusterer(clusterer);
        switch (testMode) {
            case 3:
            case 5: // Test on training

                eval.evaluateClusterer(trainInst, "", false);
                predData = setUpVisualizableInstances(inst, eval);
                outBuff
                        .append(Messages
                                .getInstance()
                                .getString(
                                        "ClustererPanel_StartClusterer_Run_OutBuffer_Text_TwentySecond"));
                break;
            default:
                throw new Exception(Messages.getInstance().getString(
                        "ClustererPanel_StartClusterer_Run_Exception_Text_Sixth"));
        }
        outBuff.append(eval.clusterResultsToString());
        outBuff.append("\n");

        return  outBuff;
    }



    private Instances removeClass(Instances inst) {
        Remove af = new Remove();
        Instances retI = null;

        try {
            if (inst.classIndex() < 0) {
                retI = inst;
            } else {
                af.setAttributeIndices("" + (inst.classIndex() + 1));
                af.setInvertSelection(false);
                af.setInputFormat(inst);
                retI = Filter.useFilter(inst, af);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retI;
    }

    /**
     * Sets up the structure for the visualizable instances. This dataset contains
     * the original attributes plus the clusterer's cluster assignments
     *
     * @param testInstances the instances that the clusterer has clustered
     * @param eval the evaluation to use
     * @return a PlotData2D object encapsulating the visualizable instances. The
     *         instances contain one more attribute (predicted cluster) than the
     *         testInstances
     */
    public static PlotData2D setUpVisualizableInstances(Instances testInstances,
                                                        ClusterEvaluation eval) throws Exception {

        int numClusters = eval.getNumClusters();
        double[] clusterAssignments = eval.getClusterAssignments();

        FastVector hv = new FastVector();
        Instances newInsts;

        Attribute predictedCluster;
        FastVector clustVals = new FastVector();

        for (int i = 0; i < numClusters; i++) {
            clustVals.addElement(Messages.getInstance().getString(
                    "ClustererPanel_SetUpVisualizableInstances_ClustVals_Text")
                    + i);
        }
        predictedCluster = new Attribute(Messages.getInstance().getString(
                "ClustererPanel_SetUpVisualizableInstances_PredictedCluster_Text"),
                clustVals);
        for (int i = 0; i < testInstances.numAttributes(); i++) {
            hv.addElement(testInstances.attribute(i).copy());
        }
        hv.addElement(predictedCluster);

        newInsts = new Instances(testInstances.relationName() + "_clustered", hv,
                testInstances.numInstances());

        double[] values;
        int j;
        int[] pointShapes = null;
        int[] classAssignments = null;
        if (testInstances.classIndex() >= 0) {
            classAssignments = eval.getClassesToClusters();
            pointShapes = new int[testInstances.numInstances()];
            for (int i = 0; i < testInstances.numInstances(); i++) {
                pointShapes[i] = Plot2D.CONST_AUTOMATIC_SHAPE;
            }
        }

        for (int i = 0; i < testInstances.numInstances(); i++) {
            values = new double[newInsts.numAttributes()];
            for (j = 0; j < testInstances.numAttributes(); j++) {
                values[j] = testInstances.instance(i).value(j);
            }
            if (clusterAssignments[i] < 0) {
                values[j] = Instance.missingValue();
            } else {
                values[j] = clusterAssignments[i];
            }
            newInsts.add(new Instance(1.0, values));
            if (pointShapes != null) {
                if (clusterAssignments[i] >= 0) {
                    if ((int) testInstances.instance(i).classValue() != classAssignments[(int) clusterAssignments[i]]) {
                        pointShapes[i] = Plot2D.ERROR_SHAPE;
                    }
                } else {
                    pointShapes[i] = Plot2D.MISSING_SHAPE;
                }
            }
        }
        PlotData2D plotData = new PlotData2D(newInsts);
        if (pointShapes != null) {
            plotData.setShapeType(pointShapes);
        }
        plotData.addInstanceNumberAttribute();
        return plotData;
    }


}
